package edu.hawaii.examschd.modules.CouchDB;

import java.net.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.StringTokenizer;
import java.io.*;
import edu.hawaii.examschd.Core;
import edu.hawaii.examschd.modules.Client;

/**
 * A client interface for CouchDB HTTP Server
 * 
 * @author johnmacmillan
 * 
 */
public class CouchDBClient extends Client {

  boolean DEBUG = false;
  boolean VERBOSE = false;

  String host = "127.0.0.1";
  String request = "/uhm/_design/uhm/_view/crn?key=\"81455\"";
  int port = 5984;
  Socket connection;
  OutputStream out;
  PrintStream ps;
  InputStream is;
  boolean connected = false;

  /*
   * Construct the CouchDBClient object
   */
  public CouchDBClient() {
  }

  /*
   * Define a non-default host address
   */
  public CouchDBClient(String h) {
    host = h;

  }

  /*
   * Define a non-default port
   */
  public CouchDBClient(int p) {
    port = checkPort(p);
  }

  /*
   * Define a non-default host and port
   */
  public CouchDBClient(String h, int p) {
    host = h;
    port = checkPort(p);
  }

  /*
   * Establish connection with the CouchDB Server
   */
  protected Socket connect() {
    try {
      connection = new Socket(host, port);
      connected = true;
      if (VERBOSE) {
        print("\r\nConnected to host " + host + ":" + port);
      }

    }
    catch (UnknownHostException uhe) {
      print("\r\nUnable to reach host at \"" + host + ":" + port);
      return null;

    }
    catch (IOException ioe) {
      return null;
    }
    if (this.isConnected()) {
      try {
        out = connection.getOutputStream();
        ps = new PrintStream(out, true); // Second param: auto-flush on
        // write = true
        // Now, you don't have to flush it, because of the auto-flush
        // flag we turned on.
        is = connection.getInputStream();
      }
      catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }
    return connection;
  }

  /**
   * Send a HTTP get request with specified extra params Returns a string in the format
   * [crn,dept,num,day,starttime,endtime]
   * 
   * @param req
   * @return
   */
  protected String send(String req) {
    String resp = null;
    this.connect();
    if (this.isConnected()) {
      if (req == null) {
        req = request;
      }
      req = construct_http_get(req);
      if (VERBOSE) {
        print("Sending request:\r\n" + req);
      }
      ps.println(req);

      // get the response
      try {
        if (VERBOSE) {
          print("waiting for response...");
        }

        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        String inputLine;
        resp = "";

        while ((inputLine = in.readLine()) != null) {
          if (VERBOSE) {
            System.out.println("Readlines:" + inputLine);
          }
          resp = resp + inputLine;
        }
        in.close();
        if (VERBOSE) {
          print("\r\n -- Closed Input Stream --\r\n");
        }
      }
      catch (IOException ioe) {
        print("ioe");
      }
      resp = construct_response(resp);
      return resp;
    }
    else {
      connection = connect();
      print("\r\nInvalid connection please try again!");
    }
    return null;
  }

  /**
   * Close the client connection
   * 
   * @return 1 if sucessful, 0 if false
   */
  protected int close() {
    try {
      print("Closing connection");
      if (this.isConnected()) {
        connection.close();
      }
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }

    return 0;
  }

  /*
   * Make sure port numbers are within range
   */
  protected int checkPort(int p) {
    if (p > 65535 || p < 1024) {
      print("Invalid port number. Using Default");
      return 5984;
    }
    else {
      return p;
    }
  }

  /**
   * Print function for standardized printing
   * 
   * @param s
   */
  private void print(String s) {
    // Core.print(s);
    PrintStream out;
    try {
      out = new PrintStream(System.out, true, "UTF-8");
      out.println(s);
    }
    catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  /**
   * Is there a valid connection?
   */
  public boolean isConnected() {
    return connected;
  }

  /**
   * Build the HTTP GET header for couchDB
   * 
   * @param req page to request
   * @return returns the completed request for sending.
   */
  protected String construct_http_get(String req) {
    String s =
        "GET " + req + " HTTP/1.1\r\n" + "Host: " + host + ":" + port + "\r\n"
            + "Connection: close\r\n" + "Cache-Control: max-age=0\r\n"
            + "User-Agent: CouchDBClient v0.2a\r\n" + "Accept: text/html\r\n"
            + "Accept-Language: en-US,en;q=0.8,ja;q=0.6" + "Accept-Encoding: gzip,deflate,sdch\r\n"
            + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n" + "\r\n";

    return s;
  }

  /**
   * Constructs a response in the format "key,value(s)"
   * 
   * @param inputLine output from couchdb request
   * @return response csv string of key and values
   */
  private String construct_response(String inputLine) {
    String response = "";
    String temp = ""; // working string
    if (DEBUG) {
      System.out.println("construct_response input:" + inputLine);
    }
    StringTokenizer st = new StringTokenizer(inputLine, "[");
    // Discard first token
    st.nextToken();

    temp = st.nextToken();
    if (temp.startsWith("]")) {
      return null;
    }

    // Not sure why i have to call it twice to kill the first token
    temp += "[" + st.nextToken();
    if (VERBOSE) {
      print("temp:" + temp);
    }
    st = new StringTokenizer(temp, "\"");
    while (st.hasMoreTokens()) {
      temp = st.nextToken();
      // find the key value
      if (temp.equals("key")) {
        st.nextToken();
        response = response + st.nextToken() + ",";
      }
      // get values and add them to the string
      if (temp.equals(":[")) {
        // format for output string
        boolean loop = true;
        while (loop) {
          temp = st.nextToken(",");
          // cut off the end characters
          if (temp.contains("\"]")) {
            temp = temp.substring(0, 6);
            loop = false;
          }
          response = response + temp.substring(1, temp.length() - 1) + ",";
        }
        response = response.substring(0, response.length() - 1);
        if (VERBOSE) {
          print(response);
        }
        return response;
      }
    }

    return response;
  }

  /**
   * Construct a calendar object from constructed response input in the format year,month,day,time
   * 
   * @param input output from construct_response
   * @return Calendar object
   */
  protected Calendar construct_exam_cal(String input) {
    StringTokenizer st = new StringTokenizer(input, ",");
    Calendar cal = new GregorianCalendar();
    DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
    String temp;
    int year, month, day, hour, minute;
    year = Integer.parseInt(st.nextToken());
    month = Integer.parseInt(st.nextToken());
    day = Integer.parseInt(st.nextToken());
    temp = st.nextToken();
    hour = Integer.parseInt(temp.substring(0, 2));
    minute = Integer.parseInt(temp.substring(2));
    cal.set(year, month, day, hour, minute);
    if (DEBUG) {
      System.out.println(dateFormat.format(cal.getTime()));
    }
    return cal;
  }

  // /**
  // * Testing method
  // */
  // public static void main(String[] args) {
  // CouchDBClient cdbc = new CouchDBClient();
  // cdbc.checkPort(65546);
  // String line = cdbc.send("/uhm/_design/uhm/_view/exam?key=\"1\"");
  // cdbc.print(line);
  // System.out.println(";----------------------------\r\n");
  // String temp = "1730";
  // System.out.print(temp.substring(0, 2));
  // System.out.print(":"+temp.substring(2)+"\r\n");
  // System.out.println(";----------------------------\r\n");
  // cdbc.construct_exam_cal("2013,5,10,0730,0930");
  // System.out.println(";----------------------------\r\n");
  // line = cdbc.send("/uhm/_design/uhm/_view/crn?key=\"84850\"");
  // cdbc.print(line);
  // line = cdbc.send("/uhm/_design/uhm/_view/crn?key=\"85833\"");
  // cdbc.print(line);
  //
  // }
}